<!--
@license
Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
-->
<link rel="import" href="boot.html">
<script>
  (function() {
    'use strict';

    /**
     * Class representing a static string value which can be used to filter
     * strings by asseting that they have been created via this class. The
     * `value` property returns the string passed to the constructor.
     */
    class LiteralString {
      constructor(string) {
        /** @type {string} */
        this.value = string.toString();
      }
      /**
       * @return {string} LiteralString string value
       */
      toString() {
        return this.value;
      }
    }

    /**
     * @param {*} value Object to stringify into HTML
     * @return {string} HTML stringified form of `obj`
     */
    function literalValue(value) {
      if (value instanceof LiteralString) {
        return /** @type {!LiteralString} */(value).value;
      } else {
        throw new Error(`non-literal value passed to Polymer.htmlLiteral: ${value}`);
      }
    }

    /**
     * @param {*} value Object to stringify into HTML
     * @return {string} HTML stringified form of `obj`
     */
    function htmlValue(value) {
      if (value instanceof HTMLTemplateElement) {
        return /** @type {!HTMLTemplateElement } */(value).innerHTML;
      } else if (value instanceof LiteralString) {
        return literalValue(value);
      } else {
        throw new Error(`non-template value passed to Polymer.html: ${value}`);
      }
    }

    /**
     * A template literal tag that creates an HTML <template> element from the
     * contents of the string.
     *
     * This allows you to write a Polymer Template in JavaScript.
     *
     * Templates can be composed by interpolating `HTMLTemplateElement`s in
     * expressions in the JavaScript template literal. The nested template's
     * `innerHTML` is included in the containing template.  The only other
     * values allowed in expressions are those returned from `Polymer.htmlLiteral`
     * which ensures only literal values from JS source ever reach the HTML, to
     * guard against XSS risks.
     *
     * All other values are disallowed in expressions to help prevent XSS
     * attacks; however, `Polymer.htmlLiteral` can be used to compose static
     * string values into templates. This is useful to compose strings into
     * places that do not accept html, like the css text of a `style`
     * element.
     *
     * Example:
     *
     *     static get template() {
     *       return Polymer.html`
     *         <style>:host{ content:"..." }</style>
     *         <div class="shadowed">${this.partialTemplate}</div>
     *         ${super.template}
     *       `;
     *     }
     *     static get partialTemplate() { return Polymer.html`<span>Partial!</span>`; }
     *
     * @memberof Polymer
     * @param {!ITemplateArray} strings Constant parts of tagged template literal
     * @param {...*} values Variable parts of tagged template literal
     * @return {!HTMLTemplateElement} Constructed HTMLTemplateElement
     */
    Polymer.html = function html(strings, ...values) {
      const template = /** @type {!HTMLTemplateElement} */(document.createElement('template'));
      template.innerHTML = values.reduce((acc, v, idx) =>
          acc + htmlValue(v) + strings[idx + 1], strings[0]);
      return template;
    };

    /**
     * An html literal tag that can be used with `Polymer.html` to compose.
     * a literal string.
     *
     * Example:
     *
     *     static get template() {
     *       return Polymer.html`
     *         <style>
     *           :host { display: block; }
     *           ${styleTemplate}
     *         </style>
     *         <div class="shadowed">${staticValue}</div>
     *         ${super.template}
     *       `;
     *     }
     *     static get styleTemplate() { return Polymer.htmlLiteral`.shadowed { background: gray; }`; }
     *
     * @memberof Polymer
     * @param {!ITemplateArray} strings Constant parts of tagged template literal
     * @param {...*} values Variable parts of tagged template literal
     * @return {!LiteralString} Constructed literal string
     */
    Polymer.htmlLiteral = function(strings, ...values) {
      return new LiteralString(values.reduce((acc, v, idx) =>
          acc + literalValue(v) + strings[idx + 1], strings[0]));
    };
  })();
</script>
